##############
# (*) Kaleidoscope
# By: Allan Spale
# May 2008

from bsddb import db
import mx.DateTime
import sys
import uuid
import types

from Kaleidoscope.KSColumn import KSColumn, KSColumnError
from Kaleidoscope.KSEventManager import KSEventManager
from Kaleidoscope.KSTransactionFactoryBSDDB import KSTransactionFactoryBSDDB
from Kaleidoscope.KSTransactionBSDDB import KSTransactionBSDDB
from Kaleidoscope.KSColumnAdapted import KSColumnAdapted
from Kaleidoscope.KSUtility import KSUtility
from Kaleidoscope.KSCursorError import KSCursorError


class KSColumnBSDDBUnique(KSColumnAdapted):
    """
    Because of complexities with associated secondary databases and because
    these secondary databases can themselves be primary databases with
    some other secondary databases attached to it...no associated secondary
    databases will be used...just to make things a bit simpler. There will be
    a KSMappedColumn class that will associate the data of the KSUniqueColumn with
    its type and the related database containing the items that are part of
    that type.

    """

    # Event IDs ... redundant, but code will be safer if constants are
    # assigned and reused throughout the code
    __KSEM_BEF_get_cursor = 140
    __KSEM_BEF_set_flags = 141
    __KSEM_BEF_open = 142
    __KSEM_BEF_close = 143
    __KSEM_BEF_get_data_from_key = 144
    __KSEM_BEF_get_keys_from_data_value = 145
    __KSEM_BEF_get_keys_from_data_object_range = 146
    __KSEM_BEF_get_data_objects_from_key_range = 147
    __KSEM_BEF_has_key = 148
    __KSEM_BEF_has_value = 149
    __KSEM_BEF_insert_item_with_key_value_pair = 150
    __KSEM_BEF_insert_value_and_generate_key = 151
    __KSEM_BEF_change_key = 152
    __KSEM_BEF_change_all_data_values = 153
    __KSEM_BEF_change_key_value_pair = 154
    __KSEM_BEF_delete_item_by_key = 155
    __KSEM_BEF_delete_items_by_value_list = 156
    __KSEM_BEF_get_all = 157
    __KSEM_BEF_values = 158
    __KSEM_BEF_keys = 159
    __KSEM_BEF_size = 160
    __KSEM_BEF_print_all = 161
    __KSEM_BEF_reorganize_keys = 162
    __KSEM_BEF_return_value_type = 163

    __KSEM_AFT_get_cursor = 1140
    __KSEM_AFT_set_flags = 1141
    __KSEM_AFT_open = 1142
    __KSEM_AFT_close = 1143
    __KSEM_AFT_get_data_from_key = 1144
    __KSEM_AFT_get_keys_from_data_value = 1145
    __KSEM_AFT_get_keys_from_data_object_range = 1146
    __KSEM_AFT_get_data_objects_from_key_range = 1147
    __KSEM_AFT_has_key = 1148
    __KSEM_AFT_has_value = 1149
    __KSEM_AFT_insert_item_with_key_value_pair = 1150
    __KSEM_AFT_insert_value_and_generate_key = 1151
    __KSEM_AFT_change_key = 1152
    __KSEM_AFT_change_all_data_values = 1153
    __KSEM_AFT_change_key_value_pair = 1154
    __KSEM_AFT_delete_item_by_key = 1155
    __KSEM_AFT_delete_items_by_value_list = 1156
    __KSEM_AFT_get_all = 1157
    __KSEM_AFT_values = 1158
    __KSEM_AFT_keys = 1159
    __KSEM_AFT_size = 1160
    __KSEM_AFT_print_all = 1161
    __KSEM_AFT_reorganize_keys = 1162
    __KSEM_AFT_return_value_type = 1163

    @property
    def is_open(self):
        return self.__b_open


    def __init__(self, u_column_name, i_column_flags, mx_start_datetime,
                  bsddb_environment, u_key_type, b_pack_key, b_unique_values):
        try:
            super(KSColumnBSDDBUnique, self).__init__(
                u_column_name, mx_start_datetime, u_key_type, b_pack_key,
                b_unique_values)

            self.__b_open = False
            self.__id = uuid.uuid4()
            self.ksem_event_manager = KSEventManager(self.__id)
            self.ksem_event_manager.create_function_event(
                u'get_cursor',
                KSColumnBSDDBUnique.__KSEM_BEF_get_cursor,
                KSColumnBSDDBUnique.__KSEM_AFT_get_cursor)
            self.ksem_event_manager.create_function_event(
                u'set_flags',
                KSColumnBSDDBUnique.__KSEM_BEF_set_flags,
                KSColumnBSDDBUnique.__KSEM_AFT_set_flags)
            self.ksem_event_manager.create_function_event(
                u'open',
                KSColumnBSDDBUnique.__KSEM_BEF_open,
                KSColumnBSDDBUnique.__KSEM_AFT_open)
            self.ksem_event_manager.create_function_event(
                u'close',
                KSColumnBSDDBUnique.__KSEM_BEF_close,
                KSColumnBSDDBUnique.__KSEM_AFT_close)
            self.ksem_event_manager.create_function_event(
                u'get_data_from_key',
                KSColumnBSDDBUnique.__KSEM_BEF_get_data_from_key,
                KSColumnBSDDBUnique.__KSEM_AFT_get_data_from_key)
            self.ksem_event_manager.create_function_event(
                u'get_keys_from_data_value',
                KSColumnBSDDBUnique.__KSEM_BEF_get_keys_from_data_value,
                KSColumnBSDDBUnique.__KSEM_AFT_get_keys_from_data_value)
            self.ksem_event_manager.create_function_event(
                u'get_keys_from_data_object_range',
                KSColumnBSDDBUnique.__KSEM_BEF_get_keys_from_data_object_range,
                KSColumnBSDDBUnique.__KSEM_AFT_get_keys_from_data_object_range)
            self.ksem_event_manager.create_function_event(
                u'get_data_objects_from_key_range',
                KSColumnBSDDBUnique.__KSEM_BEF_get_data_objects_from_key_range,
                KSColumnBSDDBUnique.__KSEM_AFT_get_data_objects_from_key_range)
            self.ksem_event_manager.create_function_event(
                u'has_key', KSColumnBSDDBUnique.__KSEM_BEF_has_key,
                KSColumnBSDDBUnique.__KSEM_AFT_has_key)
            self.ksem_event_manager.create_function_event(
                u'has_value', KSColumnBSDDBUnique.__KSEM_BEF_has_value,
                KSColumnBSDDBUnique.__KSEM_AFT_has_value)
            self.ksem_event_manager.create_function_event(
                u'insert_item_with_key_value_pair',
                KSColumnBSDDBUnique.__KSEM_BEF_insert_item_with_key_value_pair,
                KSColumnBSDDBUnique.__KSEM_AFT_insert_item_with_key_value_pair)
            self.ksem_event_manager.create_function_event(
                u'insert_value_and_generate_key',
                KSColumnBSDDBUnique.__KSEM_BEF_insert_value_and_generate_key,
                KSColumnBSDDBUnique.__KSEM_AFT_insert_value_and_generate_key)
            self.ksem_event_manager.create_function_event(
                u'change_key', KSColumnBSDDBUnique.__KSEM_BEF_change_key,
                KSColumnBSDDBUnique.__KSEM_AFT_change_key)
            self.ksem_event_manager.create_function_event(
                u'change_all_data_values',
                KSColumnBSDDBUnique.__KSEM_BEF_change_all_data_values,
                KSColumnBSDDBUnique.__KSEM_AFT_change_all_data_values)
            self.ksem_event_manager.create_function_event(
                u'change_key_value_pair',
                KSColumnBSDDBUnique.__KSEM_BEF_change_key_value_pair,
                KSColumnBSDDBUnique.__KSEM_AFT_change_key_value_pair)
            self.ksem_event_manager.create_function_event(
                u'delete_item_by_key',
                KSColumnBSDDBUnique.__KSEM_BEF_delete_item_by_key,
                KSColumnBSDDBUnique.__KSEM_AFT_delete_item_by_key)
            self.ksem_event_manager.create_function_event(
                u'delete_items_by_value_list',
                KSColumnBSDDBUnique.__KSEM_BEF_delete_items_by_value_list,
                KSColumnBSDDBUnique.__KSEM_AFT_delete_items_by_value_list)
            self.ksem_event_manager.create_function_event(
                u'get_all', KSColumnBSDDBUnique.__KSEM_BEF_get_all,
                KSColumnBSDDBUnique.__KSEM_AFT_get_all)
            self.ksem_event_manager.create_function_event(
                u'values', KSColumnBSDDBUnique.__KSEM_BEF_values,
                KSColumnBSDDBUnique.__KSEM_AFT_values)
            self.ksem_event_manager.create_function_event(
                u'keys', KSColumnBSDDBUnique.__KSEM_BEF_keys,
                KSColumnBSDDBUnique.__KSEM_AFT_keys)
            self.ksem_event_manager.create_function_event(
                u'size', KSColumnBSDDBUnique.__KSEM_BEF_size,
                KSColumnBSDDBUnique.__KSEM_AFT_size)
            self.ksem_event_manager.create_function_event(
                u'print_all', KSColumnBSDDBUnique.__KSEM_BEF_print_all,
                KSColumnBSDDBUnique.__KSEM_AFT_print_all)
            self.ksem_event_manager.create_function_event(
                u'reorganize_keys',
                KSColumnBSDDBUnique.__KSEM_BEF_reorganize_keys,
                KSColumnBSDDBUnique.__KSEM_AFT_reorganize_keys)
            self.ksem_event_manager.create_function_event(
                u'return_value_type',
                KSColumnBSDDBUnique.__KSEM_BEF_return_value_type,
                KSColumnBSDDBUnique.__KSEM_AFT_return_value_type)

            if (isinstance(i_column_flags, int)):
                self.__i_column_flags = i_column_flags
            else:
                self.__i_column_flags = 0
                KSColumnError.print_and_raise_error(
                    u'The flags parameter must be an integer.',
                    KSColumnError.B_CLASS_EXCEPTION)

            db_env_type = db.DBEnv(0) #type creator

            if (isinstance(bsddb_environment, type(db_env_type))):
                self.__bsddb_environment = bsddb_environment
                self.__db_column = db.DB(self.__bsddb_environment, 0)
            else:
                self.__bsddb_environment = None
                KSColumnError.print_and_raise_error(
                    u'A valid BSDDB environment is required for this class.',
                    KSColumnError.B_CLASS_EXCEPTION)

            if (isinstance(i_column_flags, int)):
                self.__i_column_flags = i_column_flags
            else:
                self.__i_column_flags = 0
                KSColumnError.print_and_raise_error(
                    u'The flags parameter must be an integer.',
                    KSColumnError.B_CLASS_EXCEPTION)

            # Transaction stuff
            self.kstxf_transaction_factory = \
                KSTransactionFactoryBSDDB(self.__bsddb_environment)

        except db.DBError:
            KSColumnError.print_and_raise_error(u'Could not create a '
                + u'BSDDB column.', KSColumnError.B_UNKNOWN_EXCEPTION)

    """
    def set_transaction_id(self, i_transaction_id):
        return super(KSColumnBSDDBUnique, self).set_transaction_id(
            i_transaction_id)
    """

    def get_cursor(self, kstx_parent_transaction):
        """
        Return a cursor from the database. No event for getting cursor; it is
        too little of an event to do all this stuff.

        """
        #i_size = self.size(kstx_transaction)

        #if (i_size > 0):
        cursor = self.__db_column.cursor(
            kstx_parent_transaction.obj_transaction)
        # No commit; comes later
        return cursor
        #else:
            #raise KSCursorError


    def set_flags(self, i_column_flags):
        """
        Set the flags for the database. This is not an "OR" operation; the
        entire flag set will be assigned with this function.

        """
        try:
            i_before_event_id = self.__KSEM_BEF_set_flags
            i_after_event_id = self.__KSEM_AFT_set_flags
            self.ksem_event_manager.send_event_message(
                i_before_event_id, [i_column_flags])

            u_general_error = u'Flags for the database must be ' \
                + u'stored in an integer.'
            if (isinstance(i_column_flags, int)):
                self.__i_column_flags = i_column_flags
            else:
                KSColumnError.print_and_raise_error(
                    u_general_error, KSColumnError.B_CLASS_EXCEPTION,
                    self.ksem_event_manager, i_after_event_id)

            self.ksem_event_manager.send_event_message(
                i_after_event_id, [True])
            return True

        except db.DBError:
            KSColumnError.print_and_raise_error(
                u_general_error, KSColumnError.B_UNKNOWN_EXCEPTION,
                self.ksem_event_manager, i_after_event_id)


    def open(self, i_flags, i_permissions, kstx_parent_transaction):
        """
        Open the file containing the column. To be safe, the db.DB_CREATE
        flag will be OR-ed with all the other flags. If that particular flag
        already exists, then the flag will still occur.

        """
        try:
            if not(self.__b_open):
                # Safety measure to make sure that only user and group can
                # modifythe files, while preventing access to anyone else in
                # the world.
                if (i_permissions == 0):
                    i_permissions = 0660

                kstx_transaction = self.get_transaction(kstx_parent_transaction,
                    self.open)
                i_before_event_id = self.__KSEM_BEF_open
                i_after_event_id = self.__KSEM_AFT_open
                self.ksem_event_manager.send_event_message(
                    i_before_event_id, [i_flags, i_permissions])

                self.__db_column.set_flags(self.__i_column_flags)

                if (self.u_database_name == ''):
                    # LOCAL IN-MEMORY DATABASE: will not persist after column is
                    # destroyed; transfer data somewhere else if persistence is
                    # necessary using column functions
                    self.__db_column.open(None, None,
                        db.DB_BTREE, i_flags | db.DB_CREATE, i_permissions,
                        txn=kstx_transaction.obj_transaction)
                else:
                    # DISK-BASED DATABASE: standard
                    self.__db_column.open(None, self.u_database_name,
                        db.DB_BTREE, i_flags | db.DB_CREATE, i_permissions,
                        txn=kstx_transaction.obj_transaction)

                    kstx_transaction.commit(0, self.open)
                    self.__b_open = True

                self.ksem_event_manager.send_event_message(
                    i_after_event_id, [self.__b_open])

            return self.__b_open

        except db.DBError:
            kstx_transaction.cancel()
            KSColumnError.print_and_raise_error(u'The database could not be '
                + u'opened.', KSColumnError.B_UNKNOWN_EXCEPTION,
                self.ksem_event_manager, i_after_event_id)


    def close(self):
        """
        Close the file containing the column.

        """
        try:
            if (self.__b_open):
                i_before_event_id = self.__KSEM_BEF_close
                i_after_event_id = self.__KSEM_AFT_close
                self.ksem_event_manager.send_event_message(
                    i_before_event_id, [])

                self.__db_column.close(0)
                print 'closed'
                self.__b_open = False
                self.ksem_event_manager.send_event_message(
                    i_after_event_id, [self.__b_open])
            return self.__b_open

        except db.DBError:
            KSColumnError.print_and_raise_error(u'The database could not be '
                + u'closed.', KSColumnError.B_UNKNOWN_EXCEPTION,
                self.ksem_event_manager, i_after_event_id)


    def get_data_from_key(self, obj_key, kstx_parent_transaction,
                              b_return_packed_data):
        """
        Return the data object based on an integer or string key. Integers
        will be packed according to big endian notation.

        """
        try:
            kstx_transaction = self.get_transaction(kstx_parent_transaction,
                self.get_data_from_key)
            i_before_event_id = self.__KSEM_BEF_get_data_from_key
            i_after_event_id = self.__KSEM_AFT_get_data_from_key
            self.ksem_event_manager.send_event_message(
                i_before_event_id, [obj_key, kstx_parent_transaction,
                    b_return_packed_data])

            # Prohibit searching for NULL_VALUE key and return NULL_VALUE
            if (obj_key == KSColumn.NULL_VALUE):
                self.__db_column.get = lambda x, y: KSColumn.NULL_VALUE

            packed_key = self.marshal_key(obj_key)
            if (b_return_packed_data):
                #obj_data = self.__db_column[packed_key]
                obj_data = \
                    self.__db_column.get(packed_key,
                        txn=kstx_transaction.obj_transaction)
            else:
                #obj_data = self.unpickle_data(self.__db_column[packed_key])
                obj_pickled_data = self.__db_column.get(packed_key,
                    txn=kstx_transaction.obj_transaction)
                obj_data = self.unpickle_data(obj_pickled_data)

            kstx_transaction.commit(0, self.get_data_from_key)

            self.ksem_event_manager.send_event_message(
                i_after_event_id, [obj_key, obj_data])
            return obj_data

        except Exception:
            kstx_transaction.cancel()
            KSColumnError.print_and_raise_error(u'The key was unable to '
                + u'retrieve the data.', KSColumnError.B_UNKNOWN_EXCEPTION,
                self.ksem_event_manager, i_after_event_id)


    def get_keys_from_data_value(self, obj_data, kstx_parent_transaction,
                                     b_return_packed_data):
        """
        Go through the data and find the list of keys from the given values.

        """
        # Code adapted from _db_print in dbtables.py in pybsddb
        # Allow searching for NULL VALUE; it is necessary.
        try:
            kstx_transaction = self.get_transaction(kstx_parent_transaction,
                self.get_keys_from_data_value)
            i_before_event_id = \
                self.__KSEM_BEF_get_keys_from_data_value
            i_after_event_id = \
                self.__KSEM_AFT_get_keys_from_data_value
            self.ksem_event_manager.send_event_message(
                i_before_event_id, [obj_data, b_return_packed_data])

            list_keys = []
            #i_size = self.size(kstx_transaction)

            cursor = self.get_cursor(kstx_transaction)

            obj_pickled_data = self.pickle_data(obj_data)

            if (b_return_packed_data):
                optional_data_unpacking = \
                    lambda item_packed_key, u_key_type: item_packed_key
            else:
                optional_data_unpacking = lambda item_packed_key, u_key_type: \
                    self.unmarshal_key(item_packed_key, self.u_key_type)

            item = cursor.first()

            while item:
                item_packed_key, item_pickled_data = item

                if (item_pickled_data == obj_pickled_data):
                    list_keys.append(optional_data_unpacking(
                        item_packed_key, self.u_key_type))
                # else, nothing

                item = cursor.next()
            # end loop

            cursor.close()
            kstx_transaction.commit(0, self.get_keys_from_data_value)

            self.ksem_event_manager.send_event_message(
                i_after_event_id, [obj_data, list_keys])
            return list_keys

        except db.DBNotFoundError:
            cursor.close()
            kstx_transaction.cancel()
            KSColumnError.print_and_raise_error(u'An error occurred while '
                + u'trying to retrieve the column keys from the data value.',
                KSColumnError.B_UNKNOWN_EXCEPTION,
                self.ksem_event_manager, i_after_event_id)

        except KSCursorError:
            # Database is empty, just return the empty key list
            kstx_transaction.commit(0, self.get_keys_from_data_value)
            self.ksem_event_manager.send_event_message(
                i_after_event_id, [obj_data, list_keys])
            return list_keys


    def get_keys_from_data_object_range(self, obj_start_data, obj_end_data,
            i_low_comparison_operator, i_high_comparison_operator,
            kstx_parent_transaction, b_return_packed_data):
        """
        Go through the data and find the list of keys from the given values
        in the range of data items. It is assumed that the caller will use
        a type that would support a range of data. Range is
        obj_start_data <= data_item <= obj_end_data.

        """
        # Code adapted from _db_print in dbtables.py in pybsddb
        # Allow searching for NULL VALUE; it is necessary.

        try:
            kstx_transaction = self.get_transaction(kstx_parent_transaction,
                self.get_keys_from_data_object_range)
            i_before_event_id = \
                self.__KSEM_BEF_get_keys_from_data_object_range
            i_after_event_id = \
                self.__KSEM_AFT_get_keys_from_data_object_range
            self.ksem_event_manager.send_event_message(
                i_before_event_id, [obj_start_data, obj_end_data,
                                    i_low_comparison_operator,
                                    i_high_comparison_operator,
                                    b_return_packed_data])

            list_keys = []
            cursor = self.get_cursor(kstx_transaction)
            item = cursor.first()

            # Get comparison operations
            fn_low_comparison = \
                self.get_comparison_function(i_low_comparison_operator)
            fn_high_comparison = \
                self.get_comparison_function(i_high_comparison_operator)

            #obj_pickled_start_data = self.pickle_data(obj_start_data)
            #obj_pickled_end_data = self.pickle_data(obj_end_data)

            if (b_return_packed_data):
                optional_data_unpacking = \
                    lambda item_packed_key, u_key_type: item_packed_key
            else:
                optional_data_unpacking = lambda item_packed_key, u_key_type: \
                    self.unmarshal_key(item_packed_key, self.u_key_type)

            while item:
                item_packed_key, item_pickled_data = item
                item_unpickled_data = \
                    self.unpickle_data(item_pickled_data)

                if (fn_low_comparison(obj_start_data, item_unpickled_data)
                    and
                    fn_high_comparison(item_unpickled_data, obj_end_data)):
                        list_keys.append(optional_data_unpacking(
                            item_packed_key, self.u_key_type))
                # else, nothing

                item = cursor.next()
            # end loop

            cursor.close()
            kstx_transaction.commit(0, self.get_keys_from_data_object_range)
            self.ksem_event_manager.send_event_message(
                i_after_event_id, [obj_start_data, obj_end_data,
                    i_low_comparison_operator, i_high_comparison_operator,
                    list_keys])
            return list_keys

        except db.DBNotFoundError:
            cursor.close()
            kstx_transaction.cancel()
            KSColumnError.print_and_raise_error(u'An error occurred while '
                + u'trying to retrieve the column keys from the data value.',
                KSColumnError.B_UNKNOWN_EXCEPTION,
                self.ksem_event_manager, i_after_event_id)

        except KSCursorError:
            # Database is empty; return the empty list of keys
            kstx_transaction.commit(0, self.get_keys_from_data_object_range)
            self.ksem_event_manager.send_event_message(
                i_after_event_id, [obj_start_data, obj_end_data,
                    i_low_comparison_operator, i_high_comparison_operator,
                    list_keys])
            return list_keys

        except Exception:
            cursor.close()
            kstx_transaction.cancel()
            KSColumnError.print_and_raise_error(u'An error occurred while '
                + u'trying to retrieve the column keys from the data value.',
                KSColumnError.B_UNKNOWN_EXCEPTION,
                self.ksem_event_manager, i_after_event_id)


    def get_data_objects_from_key_range(self, obj_start_key, obj_end_key,
            i_low_comparison_operator, i_high_comparison_operator,
            kstx_parent_transaction, b_return_packed_data):
        """
        Go through the keys and find the list of data objects from the given
        values in the range of keys. It is assumed that the caller will use
        a type that would support a range of data. Range is
        obj_start_key <= key <= obj_end_key.

        """
        # Code adapted from _db_print in dbtables.py in pybsddb
        # Since NULL_VALUE keys are not directly searched for in the database,
        # allow NULLs to be searched. No values will be returned instead of
        # a NULL_VALUE.

        try:
            kstx_transaction = self.get_transaction(kstx_parent_transaction,
                self.get_data_objects_from_key_range)
            i_before_event_id = \
                self.__KSEM_BEF_get_data_objects_from_key_range
            i_after_event_id = \
                self.__KSEM_AFT_get_data_objects_from_key_range
            self.ksem_event_manager.send_event_message(
                i_before_event_id, [obj_start_key, obj_end_key,
                                    i_low_comparison_operator,
                                    i_high_comparison_operator,
                                    b_return_packed_data])

            list_data_objects = []
            cursor = self.get_cursor(kstx_transaction)

            # Rare case but possible under some odd circumstance
            if (obj_start_key == KSColumn.NULL_VALUE and
                obj_end_key == KSColumn.NULL_VALUE):
                list_data_objects.append(KSColumn.NULL_VALUE)

            #obj_packed_start_key = self.marshal_key(obj_start_key)
            #obj_packed_end_key = self.marshal_key(obj_end_key)

            # Get comparison operations
            fn_low_comparison = \
                self.get_comparison_function(i_low_comparison_operator)
            fn_high_comparison = \
                self.get_comparison_function(i_high_comparison_operator)

            item = cursor.first()

            if (b_return_packed_data):
                optional_data_unpickling = \
                    lambda item_pickled_data: item_pickled_data
            else:
                optional_data_unpickling = lambda item_pickled_data: \
                    self.unpickle_data(item_pickled_data)

            while item:
                item_packed_key, item_pickled_data = item
                item_unpacked_key = self.unmarshal_key(
                    item_packed_key, self.u_key_type)

                if (fn_low_comparison(
                    obj_start_key, item_unpacked_key) and
                    fn_high_comparison(item_unpacked_key, obj_end_key)):
                        list_data_objects.append(
                            optional_data_unpickling(item_pickled_data))
                # else, nothing

                item = cursor.next()
            # end loop

            cursor.close()
            kstx_transaction.commit(0, self.get_data_objects_from_key_range)
            self.ksem_event_manager.send_event_message(
                i_after_event_id, [obj_start_key, obj_end_key,
                    i_low_comparison_operator, i_high_comparison_operator,
                    list_data_objects])
            return list_data_objects

        except db.DBNotFoundError:
            cursor.close()
            kstx_transaction.cancel()
            self.ksem_event_manager.send_event_message(i_after_event_id, [])

        except KSCursorError:
            # Database is empty; return the empty list of keys
            kstx_transaction.commit(0, self.get_keys_from_data_object_range)
            self.ksem_event_manager.send_event_message(
                i_after_event_id, [obj_start_data, obj_end_data,
                    i_low_comparison_operator, i_high_comparison_operator,
                    list_keys])
            return list_keys


    def has_key(self, obj_key, kstx_parent_transaction):
        """
        Return a boolean value that indicates whether or not the key exists
        in the database. Searching for a NULL_VALUE key will return NULL_VALUE.

        """
        try:
            kstx_transaction = self.get_transaction(kstx_parent_transaction,
                self.has_key)
            i_before_event_id = self.__KSEM_BEF_has_key
            i_after_event_id = self.__KSEM_AFT_has_key
            self.ksem_event_manager.send_event_message(
                i_before_event_id, [obj_key])

            marshalled_key = self.marshal_key(obj_key)

            #marshalled_key, txn=kstx_transaction.obj_transaction)

            if (self.size(kstx_transaction) != 0):
                if (obj_key == KSColumn.NULL_VALUE):
                    return True
                else:
                    i_result = self.__db_column.has_key(
                        marshalled_key, kstx_transaction.obj_transaction)

                    if (i_result == 1):
                        b_result = True
                    else:
                        b_result = False
            else:
                b_result = False

            kstx_transaction.commit(0, self.has_key)
            self.ksem_event_manager.send_event_message(
                i_after_event_id, [obj_key, b_result])
            return b_result

        except Exception:
            kstx_transaction.cancel()
            self.ksem_event_manager.send_event_message(
                i_after_event_id, [False])


    def has_value(self, obj_value, kstx_parent_transaction):
        """
        Return a boolean value that indicates whether or not the value exists
        at least once in the database.

        """
        try:
            # It is necessary to search for NULL_VALUE values.

            kstx_transaction = self.get_transaction(kstx_parent_transaction,
                self.has_value)
            i_before_event_id = self.__KSEM_BEF_has_value
            i_after_event_id = self.__KSEM_AFT_has_value
            self.ksem_event_manager.send_event_message(
                i_before_event_id, [obj_value])

            # Code adapted from _db_print in dbtables.py in pybsddb
            cursor = self.get_cursor(kstx_transaction)
            item = cursor.first()
            b_not_found = True

            while (b_not_found and item):
                item_packed_key, item_pickled_data = item
                item_unpickled_data = self.unpickle_data(item_pickled_data)

                if (obj_value == item_unpickled_data):
                    b_not_found = False
                # else, nothing

                item = cursor.next()

            # end loop
            cursor.close()

            kstx_transaction.commit(0, self.has_value)

            b_result = not b_not_found  # two negatives make a positive
            self.ksem_event_manager.send_event_message(
                i_after_event_id, [obj_value, b_result])
            return b_result

        except db.DBNotFoundError:
            cursor.close()
            kstx_transaction.cancel()
            self.ksem_event_manager.send_event_message(
                i_after_event_id, [obj_value, False])

        except KSCursorError:
            # The value does not exist because the database is empty.
            self.ksem_event_manager.send_event_message(
                i_after_event_id, [obj_value, False])
            return False


    def insert_item_with_key_value_pair(self, obj_key, obj_data,
                                            kstx_parent_transaction):
        """
        Insert the key/data pair. Key will be stored in big endian notation.
        self.i_transaction_id. It is OK to insert NULL values but inserting
        NULL_VALUE keys is prohibited.

        """
        try:
            kstx_transaction = self.get_transaction(kstx_parent_transaction,
                self.insert_item_with_key_value_pair)
            i_before_event_id = \
                self.__KSEM_BEF_insert_item_with_key_value_pair
            i_after_event_id = \
                self.__KSEM_AFT_insert_item_with_key_value_pair
            self.ksem_event_manager.send_event_message(
                i_before_event_id, [obj_key, obj_data])

            if (obj_key == KSColumn.NULL_VALUE):
                KSColumnError.print_and_raise_error(u'The insertion of a NULL '
                    + u'key is prohibited.', KSColumnError.B_CLASS_EXCEPTION,
                    self.ksem_event_manager, i_after_event_id)

            packed_key = self.marshal_key(obj_key)
            pickled_data = self.pickle_data(obj_data)

            if (self.value_exists_check(obj_data, kstx_transaction)):
                kstx_transaction.cancel()
                KSColumnError.print_and_raise_error(u'A constraint that '
                    + u'prohibits duplicate values was added to this column.',
                    KSColumnError.B_CLASS_EXCEPTION,
                    self.ksem_event_manager, i_after_event_id)
            else:
                # Action
                #self.__db_column[packed_key] = pickled_data
                self.__db_column.put(
                    packed_key, pickled_data, txn=kstx_transaction.obj_transaction)
                kstx_transaction.commit(0, self.insert_item_with_key_value_pair)
                self.ksem_event_manager.send_event_message(i_after_event_id,
                    [obj_key, obj_data, True])
                #print 'updated database...'
                #print self.print_all(kstx_transaction)
                return True

        except Exception:
            kstx_transaction.cancel()
            KSColumnError.print_and_raise_error(u'The key/value pair could '
                + u'not be inserted.', KSColumnError.B_UNKNOWN_EXCEPTION,
                self.ksem_event_manager, i_after_event_id)


    def insert_value_and_generate_key(self, obj_data, kstx_parent_transaction):
        """
        Insert data with time_id as a key. Key will be stored in big endian
        notation.

        """
        try:
            # NULL values are allowed, and the possibility of submitting a
            # NULL key is not possible for the user.

            kstx_transaction = self.get_transaction(kstx_parent_transaction,
                self.insert_value_and_generate_key)
            i_before_event_id = \
                self.__KSEM_BEF_insert_value_and_generate_key
            i_after_event_id = \
                self.__KSEM_AFT_insert_value_and_generate_key
            self.ksem_event_manager.send_event_message(
                i_before_event_id, [obj_data])

            if (self.value_exists_check(obj_data, kstx_transaction)):
                kstx_transaction.cancel()
                KSColumnError.print_and_raise_error(u'A constraint that '
                    + u'prohibits duplicate values was added to this column.',
                    KSColumnError.B_CLASS_EXCEPTION,
                    self.ksem_event_manager, i_after_event_id)
            else:
                obj_key = self.generate_id()

                # Backup...not needed because of internal call
                # Action
                self.insert_item_with_key_value_pair(
                    obj_key, obj_data, kstx_transaction)
                kstx_transaction.commit(0, self.insert_value_and_generate_key)
                self.ksem_event_manager.send_event_message(
                    i_after_event_id, [obj_key, obj_data, True])
                return True

        except Exception:
            kstx_transaction.cancel()
            KSColumnError.print_and_raise_error(u'The generated key and '
                + u'its specified value pair could not be inserted into '
                + u'the column.', KSColumnError.B_UNKNOWN_EXCEPTION,
                self.ksem_event_manager, i_after_event_id)


    def change_key(self, obj_old_key, obj_new_key, kstx_parent_transaction):
        """
        Get the data from the old key, and store it as a new key/value pair.
        Can be more efficient with eliminating unnecessary (un)packing and
        (un)pickling.

        """
        try:
            kstx_transaction = self.get_transaction(kstx_parent_transaction,
                self.change_key)
            i_before_event_id = self.__KSEM_BEF_change_key
            i_after_event_id = self.__KSEM_AFT_change_key
            self.ksem_event_manager.send_event_message(
                i_before_event_id, [obj_old_key, obj_new_key])

            if (obj_old_key == KSColumn.NULL_VALUE or
                obj_new_key == KSColumn.NULL_VALUE):
                KSColumnError.print_and_raise_error(u'An existing key cannot '
                    + u'be changed to a NULL value.',
                    KSColumnError.B_CLASS_EXCEPTION,
                    self.ksem_event_manager, i_after_event_id)

            # Pack the keys
            packed_old_key = self.marshal_key(obj_old_key)
            packed_new_key = self.marshal_key(obj_new_key)

            # Action: Get the old data and delete the old key
            #obj_data = self.__db_column[packed_old_key]
            #del self.__db_column[packed_old_key]
            # Action: Insert the new "changed" key
            #self.__db_column[packed_new_key] = obj_data

            obj_data = self.__db_column.get(packed_old_key,
                txn=kstx_transaction.obj_transaction)
            self.__db_column.delete(packed_old_key,
                txn=kstx_transaction.obj_transaction)
            self.__db_column.put(packed_new_key, obj_data,
                txn=kstx_transaction.obj_transaction)

            kstx_transaction.commit(0, self.change_key)
            self.ksem_event_manager.send_event_message(i_after_event_id,
                [obj_old_key, obj_new_key, True])
            return True

        except Exception:
            kstx_transaction.cancel()
            KSColumnError.print_and_raise_error(u'The key was unable to '
                + u'retrieve the data.', KSColumnError.B_UNKNOWN_EXCEPTION,
                self.ksem_event_manager, i_after_event_id)


    def change_all_data_values(self, obj_old_data_value, obj_new_data_value,
                                   kstx_parent_transaction):
        """
        Change all data values to another value. Slow way: scan the database
        and swap out the old values with the new values.

        """
        try:
            # OK to change a value to a NULL.

            kstx_transaction = self.get_transaction(kstx_parent_transaction,
                self.change_all_data_values)
            i_before_event_id = \
                self.__KSEM_BEF_change_all_data_values
            i_after_event_id = \
                self.__KSEM_AFT_change_all_data_values
            self.ksem_event_manager.send_event_message(
                i_before_event_id, [obj_old_data_value, obj_new_data_value])

            # pack the data for quicker comparison
            obj_old_pickled_data_value = self.pickle_data(obj_old_data_value)
            obj_new_pickled_data_value = self.pickle_data(obj_new_data_value)

            # Code adapted from _db_print in dbtables.py in pybsddb
            list_keys_with_changed_values = []
            cursor = self.get_cursor(kstx_transaction)
            item = cursor.first()

            while item:
                item_packed_key, item_pickled_data = item

                if (obj_old_pickled_data_value == item_pickled_data):
                    if (self.value_exists_check(
                            obj_new_data_value, kstx_transaction)):
                        cursor.close()
                        kstx_transaction.cancel()
                        KSColumnError.print_and_raise_error(u'A constraint '
                            + u'that prohibits duplicate values was added '
                            + u'to this column.',
                            KSColumnError.B_CLASS_EXCEPTION)
                    else:
                        obj_key = self.unmarshal_key(
                            item_packed_key, self.u_key_type)
                        list_keys_with_changed_values.append(obj_key)
                        cursor.delete()
                        self.__db_column.put(item_packed_key,
                            obj_new_pickled_data_value,
                            txn=kstx_transaction.obj_transaction)

                        #self.__db_column[item_packed_key] = \
                         #   obj_new_pickled_data_value
                # else, nothing

                item = cursor.next()
            # end loop

            cursor.close()
            kstx_transaction.commit(0, self.change_all_data_values)
            self.ksem_event_manager.send_event_message(i_after_event_id,
                [obj_old_data_value, obj_new_data_value,
                 list_keys_with_changed_values, True])
            return True

        except db.DBNotFoundError:
            cursor.close()
            kstx_transaction.cancel()
            KSColumnError.print_and_raise_error(u'The values specified in '
                + u'the value list could not be applied as changes to the '
                + u'column.', KSColumnError.B_UNKNOWN_EXCEPTION,
                self.ksem_event_manager, i_after_event_id)

        except KSCursorError:
            # No cursor, no problem! Just return an empty list.
            kstx_transaction.commit(0, self.change_all_data_values)
            self.ksem_event_manager.send_event_message(i_after_event_id,
                [obj_old_data_value, obj_new_data_value,
                 list_keys_with_changed_values, True])
            return True



    def change_key_value_pair(self, obj_key, obj_data, kstx_parent_transaction):
        """
        Replace the old value of the obj_key with the new value listed in
        obj_data. To be safe, disallow this operation when obj_key is NULL.

        """
        try:
            kstx_transaction = self.get_transaction(kstx_parent_transaction,
                self.change_key_value_pair)
            i_before_event_id = \
                self.__KSEM_BEF_change_key_value_pair
            i_after_event_id = \
                self.__KSEM_AFT_change_key_value_pair
            self.ksem_event_manager.send_event_message(
                i_before_event_id, [obj_key, obj_data])

            if (obj_key == KSColumn.NULL_VALUE):
                KSColumnError.print_and_raise_error(u'An existing key cannot '
                    + u'be a NULL value.',
                    KSColumnError.B_CLASS_EXCEPTION,
                    self.ksem_event_manager, i_after_event_id)

            packed_key = self.marshal_key(obj_key)
            pickled_data = self.pickle_data(obj_data)

            # FOR THE CASE OF A COLUMN THAT MUST HAVE UNIQUE VALUES...
            # This is a bit more complicated because the value changed could
            # be exactly the same as before for whatever reason. So, an
            # additional check needs to be performed that guarantees that
            # the key of the value item and the obj_key are the same. Then
            # the constraint is maintained an the value can be "changed".
            if (self.value_exists_check(obj_data, kstx_transaction)):
                list_keys = self.get_keys_from_data_value(
                    obj_data, kstx_transaction, KSColumn.B_REGULAR_DATA)

                # Since the first if was executed, it is known that there is
                # at least one key that matches the criteria
                i_key_count = len(list_keys)

                if (i_key_count == 1):
                    obj_found_key = list_keys.pop()

                    if (obj_found_key != obj_key):
                        # Constraint failed, an attempt is being made to add
                        # a duplicate value
                        kstx_transaction.cancel()
                        KSColumnError.print_and_raise_error(u'A constraint '
                            + u'that prohibits duplicate values was added '
                            + u'to this column.', KSColumnError.B_CLASS_EXCEPTION,
                            self.ksem_event_manager, i_after_event_id)
                    # else, pass through
                else:
                    KSColumnError.print_and_raise_error(u'A constraint '
                        + u'that prohibits duplicate values was added '
                        + u'to this column but somehow failed.',
                        KSColumnError.B_CLASS_EXCEPTION,
                        self.ksem_event_manager, i_after_event_id)
            # else, just pass through to the bottom
            # end if

            # Action
            #del self.__db_column[packed_key]
            #self.__db_column[packed_key] = pickled_data
            self.__db_column.delete(packed_key,
                txn=kstx_transaction.obj_transaction)
            self.__db_column.put(packed_key, pickled_data,
                txn=kstx_transaction.obj_transaction)
            kstx_transaction.commit(0, self.change_key_value_pair)
            self.ksem_event_manager.send_event_message(i_after_event_id,
                [obj_key, obj_data, True])
            return True

        except db.DBNotFoundError:
            kstx_transaction.cancel()
            KSColumnError.print_and_raise_error(u'The key could not be '
                + u'updated with a new value.',
                KSColumnError.B_UNKNOWN_EXCEPTION,
                self.ksem_event_manager, i_after_event_id)


    def delete_item_by_key(self, obj_key, kstx_parent_transaction):
        """
        Delete the item represented by the given key.

        """
        try:
            kstx_transaction = self.get_transaction(kstx_parent_transaction,
                self.delete_item_by_key)
            i_before_event_id = self.__KSEM_BEF_delete_item_by_key
            i_after_event_id = self.__KSEM_AFT_delete_item_by_key
            self.ksem_event_manager.send_event_message(
                i_before_event_id, [obj_key])

            if (obj_key == KSColumn.NULL_VALUE):
                KSColumnError.print_and_raise_error(u'The deletion of a NULL '
                    + u'key is not allowed.',
                    KSColumnError.B_CLASS_EXCEPTION,
                    self.ksem_event_manager, i_after_event_id)

            # Action
            u_key = self.marshal_key(obj_key)
            #del self.__db_column[u_key]
            self.__db_column.delete(u_key, txn=kstx_transaction.obj_transaction)
            kstx_transaction.commit(0, self.delete_item_by_key)

            self.ksem_event_manager.send_event_message(i_after_event_id,
                [obj_key, True])
            return True

        except Exception:
            kstx_transaction.cancel()
            KSColumnError.print_and_raise_error(u'The key could not be '
                + u'deleted.', KSColumnError.B_UNKNOWN_EXCEPTION,
                self.ksem_event_manager, i_after_event_id)


    def delete_items_by_value_list(self, list_values, kstx_parent_transaction):
        """
        Delete any items having a certain data value. Slow way:
        scan the database and swap out the old values with the new values.

        """
        # Code adapted from _db_print in dbtables.py in pybsddb
        # Ok to delete items if they are NULL.

        try:
            kstx_transaction = self.get_transaction(kstx_parent_transaction,
                self.delete_items_by_value_list)
            i_before_event_id = \
                self.__KSEM_BEF_delete_items_by_value_list
            i_after_event_id = \
                self.__KSEM_AFT_delete_items_by_value_list
            self.ksem_event_manager.send_event_message(
                i_before_event_id, [list_values])

            # Pickle the list of items
            list_deleted_keys = []
            list_pickled_values = []
            for value in list_values:
                list_pickled_values.append(self.pickle_data(value))

            cursor = self.get_cursor(kstx_transaction)
            item = cursor.first()

            while item:
                item_packed_key, item_pickled_data = item

                if (item_pickled_data in list_pickled_values):
                    # Action
                    list_deleted_keys.append(
                        self.unpack_key(item_packed_key, self.u_key_type))
                    cursor.delete()

                # else, nothing

                item = cursor.next()
            # end loop

            cursor.close()
            kstx_transaction.commit(0, self.delete_items_by_value_list)
            self.ksem_event_manager.send_event_message(i_after_event_id,
                [list_values, list_deleted_keys, True])
            return True

        except db.DBNotFoundError:
            cursor.close()
            kstx_transaction.cancel()
            KSColumnError.print_and_raise_error(u'The items in the value list '
                + u' could not be deleted.', KSColumnError.B_UNKNOWN_EXCEPTION,
                self.ksem_event_manager, i_after_event_id)

        except KSCursorError:
            # The transaction is ok, but no keys were deleted because none
            # were in the database.
            kstx_transaction.commit(0, self.delete_items_by_value_list)
            self.ksem_event_manager.send_event_message(i_after_event_id,
                [list_values, list_deleted_keys, True])
            return True


    def reorganize_keys(self, kstx_parent_transaction):
        """
        Think of this like defragmenting a hard drive except that this function
        will consolidate gaps in IDs and free up "lost" space that was created
        with the passage of time. The reorganization is dependent upon the
        structure of column data store and requires that iteration through
        the list of keys is done in ASCENDING order [lowest to highest].
        Although it seems obvious, this function is only suitable for auto-
        generated integer or long IDs.

        All data must be throughly backed up using transactions. Because all
        changes are a part of a single transaction block, code from the
        function change_key_value_pair will be duplicated and send events
        will occur for every data change.

        """
        try:
            kstx_transaction = self.get_transaction(kstx_parent_transaction,
                self.reorganize_keys)
            i_before_event_id = self.__KSEM_BEF_reorganize_keys
            i_after_event_id = self.__KSEM_AFT_reorganize_keys
            self.ksem_event_manager.send_event_message(i_before_event_id, [])

            cursor = self.get_cursor(kstx_transaction)
            item = cursor.first()
            i_new_id = -sys.maxint

            while item:
                item_packed_key, item_pickled_data = item
                obj_old_key = self.unmarshal_key(
                    item_packed_key, self.u_key_type)

                if (i_new_id != obj_old_key):
                    # Action: Delete current key and insert as a new key
                    #del self.__db_column[obj_old_key]
                    self.__db_column.delete(obj_old_key,
                        txn=kstx_transaction.obj_transaction)
                    packed_new_key = self.marshal_key(i_new_id)
                    obj_data = self.unpickle_data(item_pickled_data)
                    self.__db_column.put(packed_new_key, obj_data,
                        txn=kstx_transaction.obj_transaction)
                    #self.__db_column[packed_new_key] = obj_data

                    # Send out event notification to listening columns
                    self.ksem_event_manager.send_event_message(
                        i_after_event_id, [obj_old_key, i_new_id])
                # else, nothing

                # Move cursor and increment ID counter
                i_new_id = i_new_id + 1
                item = cursor.next()

            # end loop
            cursor.close()
            kstx_transaction.commit(0, self.reorganize_keys)

            # Now, recalculate the new expiration time for the column
            i_remaining_id_count = \
                int((sys.maxint - self.size()) / KSUtility.MAX_IDS_PER_SECOND)
            i_available_id_count = \
                i_remaining_id_count - KSColumn.MIN_REMAINING_IDS

            mx_new_start_datetime = self.mx_start_datetime + \
                mx.DateTime.DateTimeDeltaFromSeconds(i_remaining_id_count)
            mx_new_last_datetime = mx_new_start_datetime + \
                mx.DateTime.DateTimeDeltaFromSeconds(i_available_id_count)

            self.change_datetimes(mx_new_start_datetime, mx_new_last_datetime)

        except Exception:
            cursor.close()
            kstx_transaction.cancel()

        except KSCursorError:
            # Database is empty, no keys were reorganized
            pass


    def return_value_type(self, kstx_parent_transaction):
        """
        Return the data type of the stored value. This may be somewhat
        pointless if binary strings of data are stored. The pickling procedure
        for storing values should retrain the original object type.

        """
        try:
            kstx_transaction = self.get_transaction(kstx_parent_transaction,
                self.return_value_type)
            i_before_event_id = self.__KSEM_BEF_return_value_type
            i_after_event_id = self.__KSEM_AFT_return_value_type
            self.ksem_event_manager.send_event_message(i_before_event_id, [])

            cursor = self.get_cursor(kstx_transaction)
            item = cursor.first()
            item_packed_key, item_pickled_data = item
            type_obj = type(self.unpickle_data(item_pickled_data))
            cursor.close()

            kstx_transaction.commit(0, self.return_value_type)
            self.ksem_event_manager.send_event_message(i_after_event_id,
                [type_obj])
            return type_obj

        except db.DBNotFoundError:
            cursor.close()
            kstx_transaction.cancel()
            KSColumnError.print_and_raise_error(u'The type of the value could '
                + u'not be retrieved.', KSColumnError.B_UNKNOWN_EXCEPTION,
                self.ksem_event_manager, i_after_event_id)

        except KSCursorError:
            # Since the database is empty, it is hard to reconstruct a type
            # based on something that is not there. No string type exists
            # anywhere in this class...so...have to hope for the best by
            # returning a NoneType.
            return type(None)


    def get_all(self, kstx_parent_transaction, b_return_packed_data):
        """
        Unpack the database and return the values.

        """
        try:
            kstx_transaction = self.get_transaction(kstx_parent_transaction,
                self.get_all)
            i_before_event_id = self.__KSEM_BEF_get_all
            i_after_event_id = self.__KSEM_AFT_get_all
            self.ksem_event_manager.send_event_message(
                i_before_event_id, [b_return_packed_data])

            dict_database = {}
            if (b_return_packed_data):
                for a_tuple in self.__db_column.items(
                    kstx_transaction.obj_transaction):
                        dict_database[a_tuple[0]] = a_tuple[1]
            else:
                for a_tuple in self.__db_column.items(
                    kstx_transaction.obj_transaction):
                        dict_database[self.unmarshal_key(a_tuple[0],
                            self.u_key_type)] = \
                            self.unpickle_data(a_tuple[1])

            kstx_transaction.commit(0, self.get_all)
            self.ksem_event_manager.send_event_message(
                i_after_event_id, [dict_database])
            #print 'database of stuff = ', dict_database
            return dict_database

        except db.DBNotFoundError:
            kstx_transaction.cancel()
            KSColumnError.print_and_raise_error(u'The keys and values of '
                + u'the database could not be retrieved.',
                KSColumnError.B_UNKNOWN_EXCEPTION, self.ksem_event_manager,
                i_after_event_id)


    def values(self, kstx_parent_transaction, b_return_packed_data):
        """
        Unpack the values list and return it.

        """
        #print 'values: ',self.__db_column.values()
        try:
            kstx_transaction = self.get_transaction(kstx_parent_transaction,
                self.values)
            i_before_event_id = self.__KSEM_BEF_values
            i_after_event_id = self.__KSEM_AFT_values
            self.ksem_event_manager.send_event_message(
                i_before_event_id, [b_return_packed_data])

            list_values = []
            if (b_return_packed_data):
                for value in self.__db_column.values(
                    kstx_transaction.obj_transaction):
                        list_values.append(value)
            else:
                for value in self.__db_column.values(
                    kstx_transaction.obj_transaction):
                        list_values.append(self.unpickle_data(value))

            kstx_transaction.commit(0, self.values)
            self.ksem_event_manager.send_event_message(
                i_after_event_id, [list_values])
            return list_values

        except db.DBNotFoundError:
            kstx_transaction.cancel()
            KSColumnError.print_and_raise_error(u'The values of the database '
                + u'could not be retrieved.', KSColumnError.B_UNKNOWN_EXCEPTION,
                self.ksem_event_manager, i_after_event_id)


    def keys(self, kstx_parent_transaction, b_return_packed_data):
        """
        Unpack the keys list and return it.

        """
        #print 'keys: ',self.__db_column.keys()
        try:
            kstx_transaction = self.get_transaction(kstx_parent_transaction,
                self.keys)
            i_before_event_id = self.__KSEM_BEF_keys
            i_after_event_id = self.__KSEM_AFT_keys
            self.ksem_event_manager.send_event_message(
                i_before_event_id, [b_return_packed_data])

            list_keys = []
            if (b_return_packed_data):
                for key in self.__db_column.keys(
                    kstx_transaction.obj_transaction):
                        list_keys.append(key)
            else:
                for key in self.__db_column.keys(
                    kstx_transaction.obj_transaction):
                        list_keys.append(self.unmarshal_key(key, self.u_key_type))

            kstx_transaction.commit(0, self.keys)
            self.ksem_event_manager.send_event_message(
                i_after_event_id, [list_keys])
            return list_keys

        except db.DBNotFoundError:
            kstx_transaction.cancel()
            KSColumnError.print_and_raise_error(u'The values of the database  '
                + u'could not be retrieved.', KSColumnError.B_UNKNOWN_EXCEPTION,
                self.ksem_event_manager, i_after_event_id)


    def size(self, kstx_parent_transaction):
        try:
            kstx_transaction = self.get_transaction(kstx_parent_transaction,
                self.size)
            i_before_event_id = self.__KSEM_BEF_size
            i_after_event_id = self.__KSEM_AFT_size
            self.ksem_event_manager.send_event_message(
                i_before_event_id, [])

            rows = self.__db_column.stat(
                txn=kstx_transaction.obj_transaction)['nkeys']
            kstx_transaction.commit(0, self.size)
            self.ksem_event_manager.send_event_message(
                i_after_event_id, [rows])
            return rows

        except db.DBError:
            kstx_transaction.cancel()
            KSColumnError.print_and_raise_error(u'The size of the database  '
                + u'could not be determined.', KSColumnError.B_UNKNOWN_EXCEPTION,
                self.ksem_event_manager, i_after_event_id)


    def print_all(self, kstx_parent_transaction):
        try:
            kstx_transaction = self.get_transaction(kstx_parent_transaction,
                self.print_all)
            i_before_event_id = self.__KSEM_BEF_print_all
            i_after_event_id = self.__KSEM_AFT_print_all
            self.ksem_event_manager.send_event_message(
                i_before_event_id, [])

            everything = self.get_all(kstx_transaction, KSColumn.B_REGULAR_DATA)

            #print everything
            for item in everything:
                print item, everything[item]

            kstx_transaction.commit(0, self.print_all)
            self.ksem_event_manager.send_event_message(i_after_event_id, [True])
            return True

        except Exception:
            kstx_transaction.cancel()
            KSColumnError.print_and_raise_error(u'The values of the database '
                + u'could not be retrieved.', KSColumnError.B_UNKNOWN_EXCEPTION,
                self.ksem_event_manager, i_after_event_id)



    def factory_function(self, u_column_name, i_column_flags,
                         mx_start_datetime, u_key_type,
                         b_pack_key, b_unique_values):

        return KSColumnBSDDBUnique(u_column_name, i_column_flags,
                                   mx_start_datetime, self.__bsddb_environment,
                                   u_key_type, b_pack_key, b_unique_values)


    def get_transaction(self, kstx_parent_transaction, fn_caller):
        """
        All of the functions in this class should be treated as "atomic"
        operations. For this reason, the use of nested transactions is not
        very useful because it would permit partial commits which would just
        end up corrupting the database at higher levels that use this class.

        """
        if (isinstance(kstx_parent_transaction, KSTransactionBSDDB)):
            return kstx_parent_transaction
        elif (kstx_parent_transaction == None):
            return self.kstxf_transaction_factory.create_transaction(
                None, 0, fn_caller)
        else:
            KSColumnError.print_and_raise_error(u'A parent transaction must ' \
                + u'be of type KSTransactionBSDDB or given None.',
                KSColumnError.B_UNKNOWN_EXCEPTION, None, None)